#pragma once

#include "Vec2.h"

class Mat2
{
	Vec2  _m[2];

public:
	Mat2( const float d = float(1.0) )
	{
		_m[0].x = d;
		_m[1].y = d;
	}

	Mat2( const Vec2& a, const Vec2& b )
	{
		_m[0] = a;
		_m[1] = b;
	}

	Mat2( float m00, float m10, float m01, float m11 )
	{
		_m[0] = Vec2( m00, m01 );
		_m[1] = Vec2( m10, m11 );
	}

	Mat2( const Mat2& m )
	{
		if ( *this != m ) {
			_m[0] = m._m[0];
			_m[1] = m._m[1];
		} 
	}

	Mat2 transpose() const
	{
		return Mat2(
			_m[0][0], _m[1][0],
			_m[0][1], _m[1][1]
		);
	}

	Vec2& operator [] ( int i ) { return _m[i]; }
	const Vec2& operator [] ( int i ) const { return _m[i]; }

	Mat2 operator + ( const Mat2& m ) const
	{
		return Mat2( _m[0]+m[0], _m[1]+m[1] );
	}

	Mat2 operator - ( const Mat2& m ) const
	{
		return Mat2( _m[0]-m[0], _m[1]-m[1] );
	}

	Mat2 operator * ( const float s ) const 
	{
		return Mat2( s*_m[0], s*_m[1] );
	}

	Mat2 operator / ( const float s ) const
	{
		float r = float(1.0) / s;
		return *this * r;
	}

	friend Mat2 operator * ( const float s, const Mat2& m )
	{
		return m * s;
	}

	Mat2 operator * ( const Mat2& m ) const
	{
		Mat2  a( 0.0 );

		for ( int i = 0; i < 2; ++i ) {
			for ( int j = 0; j < 2; ++j ) {
				for ( int k = 0; k < 2; ++k ) {
					a[i][j] += _m[i][k] * m[k][j];
				}
			}
		}

		return a;
	}

	Mat2& operator += ( const Mat2& m )
	{
		_m[0] += m[0];  _m[1] += m[1];  
		return *this;
	}

	Mat2& operator -= ( const Mat2& m )
	{
		_m[0] -= m[0];  _m[1] -= m[1];
		return *this;
	}

	Mat2& operator *= ( const float s )
	{
		_m[0] *= s;  _m[1] *= s;   
		return *this;
	}

	Mat2& operator *= ( const Mat2& m )
	{
		Mat2  a( 0.0 );

		for ( int i = 0; i < 2; ++i ) {
			for ( int j = 0; j < 2; ++j ) {
				for ( int k = 0; k < 2; ++k ) {
					a[i][j] += _m[i][k] * m[k][j];
				}
			}
		}

		return *this = a;
	}

	Mat2& operator /= ( const float s )
	{
		float r = float(1.0) / s;
		return *this *= r;
	}

	Vec2 operator * ( const Vec2& v ) const // m * v
	{
		return Vec2(
			_m[0][0]*v.x + _m[0][1]*v.y,
			_m[1][0]*v.x + _m[1][1]*v.y
		);
	}

	friend std::ostream& operator << ( std::ostream& os, const Mat2& m )
	{
		return os << std::endl << m[0] << std::endl << m[1] << std::endl;
	}

	friend std::istream& operator >> ( std::istream& is, Mat2& m )
	{
		return is >> m._m[0] >> m._m[1];
	}

	operator const float* () const
	{
		return static_cast<const float*>( &_m[0].x );
	}

	operator float* ()
	{
		return static_cast<float*>( &_m[0].x );
	}
};